// Package util
// @Title util[简述该包的主要作用]
// @Description
// @Author  cnHuaShao
// @since v0.0.1
// @Version v1.0.0
// @Date 2024/4/21 21:48
// @Update 2024/4/21 21:48
package util

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/des"
	"crypto/md5"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/x509"
	"encoding/base64"
	"encoding/hex"
	"encoding/pem"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"
)

var SysConfigDir = ""

const (
	MD5_SALT = "cnhuashao_2024"
)

var (
	priKey *rsa.PrivateKey
	pubKey *rsa.PublicKey
)

// initFileKye
// @Description: 初始秘钥文件
// @return error
func initFileKye() error {
	path := SysConfigDir + "private.pem"
	// 打开文件
	file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer file.Close()
	// 获取文件内容
	info, err := file.Stat()
	if err != nil {
		return err
	}
	buf := make([]byte, info.Size())
	file.Read(buf)
	// pem解码
	block, _ := pem.Decode(buf)
	// X509解码
	priKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
	if err != nil {
		return err
	}

	path2 := SysConfigDir + "public.pem"
	// 打开文件
	file2, err := os.Open(path2)
	if err != nil {
		return err
	}
	defer file2.Close()
	// 读取文件的内容
	info2, _ := file2.Stat()
	buf2 := make([]byte, info2.Size())
	file2.Read(buf2)
	// pem解码
	block2, _ := pem.Decode(buf2)
	// x509解码
	publicKeyInterface, err := x509.ParsePKIXPublicKey(block2.Bytes)
	if err != nil {
		return err
	}
	// 类型断言
	pubKey = publicKeyInterface.(*rsa.PublicKey)
	return nil
}

// InitEncrypt
// @Description: 初始化秘钥
func InitEncrypt() error {
	t := checkFileIsExist(SysConfigDir + "private.pem")
	if !t {
		log.Println("--------当前加密配置文件不存在，开始进行创建")
		GenerateRSAKey(1024 * 4)
	}
	return initFileKye()
}

// GenerateRSAKey
// 生成RSA私钥和公钥，保存到文件中
func GenerateRSAKey(bits int) {
	// GenerateKey函数使用随机数据生成器random生成一对具有指定字位数的RSA密钥
	// Reader是一个全局、共享的密码用强随机数生成器
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		panic(err)
	}
	// 保存私钥
	// 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
	X509PrivateKey := x509.MarshalPKCS1PrivateKey(privateKey)
	// 使用pem格式对x509输出的内容进行编码
	// 创建文件保存私钥
	privateFile, err := os.Create(SysConfigDir + "private.pem")
	if err != nil {
		panic(err)
	}
	defer privateFile.Close()
	// 构建一个pem.Block结构体对象
	privateBlock := pem.Block{Type: "RSA Private Key", Bytes: X509PrivateKey}
	// 将数据保存到文件
	pem.Encode(privateFile, &privateBlock)
	// 保存公钥
	// 获取公钥的数据
	publicKey := privateKey.PublicKey
	// X509对公钥编码
	X509PublicKey, err := x509.MarshalPKIXPublicKey(&publicKey)
	if err != nil {
		panic(err)
	}
	// pem格式编码
	// 创建用于保存公钥的文件
	publicFile, err := os.Create(SysConfigDir + "public.pem")
	if err != nil {
		panic(err)
	}
	defer publicFile.Close()
	// 创建一个pem.Block结构体对象
	publicBlock := pem.Block{Type: "RSA Public Key", Bytes: X509PublicKey}
	// 保存到文件
	pem.Encode(publicFile, &publicBlock)
}

// RSADecryptHTTP
// @Description: 前端加密信息解密逻辑
// 前端加密通常外层还有一层base64，因此先进行转换，后再进行解密逻辑
// @param data
// @return string 解密结果
// @return error
func RSADecryptHTTP(data string) (string, error) {
	// 转成base64
	key, err := base64.StdEncoding.DecodeString(data)
	if err != nil {
		fmt.Println("base64.RawURLEncoding.DecodeString : ", err.Error())
		return "", err
	}
	return RSA_DecryptLong(key)
}

// RSA_DecryptLong
// @Description: 解密逻辑
// @param cipherText
// @param paths
// @return string
// @return error
func RSA_DecryptLong(cipherText []byte, paths ...string) (string, error) {
	partLen := pubKey.N.BitLen() / 8
	chunks := split(cipherText, partLen)
	buffer := bytes.NewBufferString("")
	for _, chunk := range chunks {
		// 对密文进行解密
		plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, chunk)
		if err != nil {
			return "", err
		}
		buffer.Write(plainText)
	}
	// 返回明文
	return buffer.String(), nil
}

func split(buf []byte, lim int) [][]byte {
	var chunk []byte
	chunks := make([][]byte, 0, len(buf)/lim+1)
	for len(buf) >= lim {
		chunk, buf = buf[:lim], buf[lim:]
		chunks = append(chunks, chunk)
	}
	if len(buf) > 0 {
		chunks = append(chunks, buf[:len(buf)])
	}
	return chunks
}

// RSA_Encrypt
// @Description: RSA加密，当前端传过来时需要使用
// @param plainText
// @param path
// @return []byte
func RSA_Encrypt(plainText []byte, paths ...string) []byte {
	// 对明文进行加密
	cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, plainText)
	if err != nil {
		panic(err)
	}
	// 返回密文
	return cipherText
}

// RSA_Decrypt
// @Description: RSA解密
// @param cipherText
// @param paths
// @return []byte
// @return error
func RSA_Decrypt(cipherText []byte, paths ...string) ([]byte, error) {
	// 对密文进行解密
	plainText, err := rsa.DecryptPKCS1v15(rand.Reader, priKey, cipherText)
	if err != nil {
		return nil, err
	}
	// 返回明文
	return plainText, nil
}

// Md5Crypt 加盐MD5加密
// @params str 需要加密的字符串
// @params salt interface{} 加密的盐
// @return str 返回md5码
func Md5Crypt(str string, salt ...interface{}) (CryptStr string) {
	if l := len(salt); l > 0 {
		slice := make([]string, l+1)
		str = fmt.Sprintf(str+strings.Join(slice, "%v"), salt...)
	}
	return fmt.Sprintf("%x", md5.Sum([]byte(str)))
}

// MD5Bytes 数据块MD5计算函数
func MD5Bytes(s []byte) string {
	ret := md5.Sum(s)
	return fmt.Sprintf("%x", ret)
}

// MD5 计算字符串MD5值
func MD5(s string) string {
	return MD5Bytes([]byte(s))
}

// EncryptAES_CBC
// @Description: AES CBC 加密
// @param src
// @param key
// @param iv
// @return string
// @return error
func EncryptAES_CBC(src, key, iv string) (string, error) {
	data := []byte(src)
	keyByte := []byte(key)
	block, err := aes.NewCipher(keyByte)
	if err != nil {
		println(err.Error())
		return "", err
	}
	data = PKCS5Padding(data, block.BlockSize())
	encrypted := make([]byte, len(data))
	ivByte := []byte(iv)
	aesEncrypter := cipher.NewCBCEncrypter(block, ivByte)
	aesEncrypter.CryptBlocks(encrypted, data)
	base64PW := base64.StdEncoding.EncodeToString(encrypted)
	return base64PW, nil
}

// EncryptDES_CBC CBC加密
func EncryptDES_CBC(src, key string, iv string) string {
	data := []byte(src)
	keyByte := []byte(key)
	block, err := des.NewTripleDESCipher(keyByte)
	if err != nil {
		panic(err)
	}
	data = PKCS5Padding(data, block.BlockSize())
	// 获取CBC加密模式
	ivByte := []byte(iv)
	mode := cipher.NewCBCEncrypter(block, ivByte)
	out := make([]byte, len(data))
	mode.CryptBlocks(out, data)
	return fmt.Sprintf("%X", out)
}

// DecryptDES_CBC
// CBC解密
func DecryptDES_CBC(src, key string, iv string) string {
	keyByte := []byte(key)
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	block, err := des.NewTripleDESCipher(keyByte)
	if err != nil {
		panic(err)
	}
	ivByte := []byte(iv)
	mode := cipher.NewCBCDecrypter(block, ivByte)
	plaintext := make([]byte, len(data))
	mode.CryptBlocks(plaintext, data)
	plaintext = PKCS5UnPadding(plaintext)
	return string(plaintext)
}

// EncryptDES_ECB
// @Description: ECB加密
// @param src
// @param key
// @return string
func EncryptDES_ECB(src, key string) string {
	data := []byte(src)
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	bs := block.BlockSize()
	// 对明文数据进行补码
	data = PKCS5Padding(data, bs)
	if len(data)%bs != 0 {
		panic("Need a multiple of the blocksize")
	}
	out := make([]byte, len(data))
	dst := out
	for len(data) > 0 {
		// 对明文按照blocksize进行分块加密
		// 必要时可以使用go关键字进行并行加密
		block.Encrypt(dst, data[:bs])
		data = data[bs:]
		dst = dst[bs:]
	}
	return fmt.Sprintf("%X", out)
}

// DecryptDES_ECB
// @Description: ECB解密
// @param src
// @param key
// @return string
func DecryptDES_ECB(src, key string) string {
	data, err := hex.DecodeString(src)
	if err != nil {
		panic(err)
	}
	keyByte := []byte(key)
	block, err := des.NewCipher(keyByte)
	if err != nil {
		panic(err)
	}
	bs := block.BlockSize()
	if len(data)%bs != 0 {
		panic("crypto/cipher: input not full blocks")
	}
	out := make([]byte, len(data))
	dst := out
	for len(data) > 0 {
		block.Decrypt(dst, data[:bs])
		data = data[bs:]
		dst = dst[bs:]
	}
	out = PKCS5UnPadding(out)
	return string(out)
}

// PKCS5Padding
// @Description: 明文补码算法
// @param ciphertext
// @param blockSize
// @return []byte
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
	return append(ciphertext, padtext...)
}

// PKCS5UnPadding
// @Description: 明文减码算法
// @param origData
// @return []byte
func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

// LDAPPassword
// @Description: 获取LDAP加密后的密码信息
// @param password
// @return string
func LDAPPassword(password string) string {
	// 返回base64编码密码
	base64PW := base64.StdEncoding.EncodeToString(HexToByte(password))
	return fmt.Sprintf("{MD5}%s", base64PW)
}

// HexToByte
// @Description: 基于php的pack H*替换函数
// @param hex
// @return []byte
func HexToByte(hex string) []byte {
	length := len(hex) / 2
	slice := make([]byte, length)
	rs := []rune(hex)

	for i := 0; i < length; i++ {
		s := string(rs[i*2 : i*2+2])
		value, _ := strconv.ParseInt(s, 16, 10)
		slice[i] = byte(value & 0xFF)
	}
	return slice
}

// SHA1
// @Description: sha1加密
// @param s
// @return string
func SHA1(s string) string {
	o := sha1.New()
	o.Write([]byte(s))
	return hex.EncodeToString(o.Sum(nil))

}
